#include <assert.h>

#include "GLSLProgram.h"

#include <iostream>
using namespace PBVP;

GLSLProgram::GLSLProgram(GLSLShader* vertShader, GLSLShader* fragShader)
: m_vertShader(vertShader), m_fragShader(fragShader)	
{
	init();
}

void GLSLProgram::init() {
	m_isValid = false;
	m_programId = glCreateProgram();
	if (m_programId == 0) // Failed
	{
#ifdef _DEBUG
		std::cerr << "Failed to create a GLSL program\n";
#endif
		return;
	}
	glAttachShader(m_programId, m_vertShader->shaderId());
	glAttachShader(m_programId, m_fragShader->shaderId());

	glLinkProgram(m_programId);
	GLint status;
	glGetProgramiv(m_programId, GL_LINK_STATUS, &status);
	if (status != GL_TRUE)
	{
#ifdef _DEBUG
		std::cerr << "Failed to link the GLSL program\n";
#endif
		return;
	}
	// now set program to valid
	m_isValid = true;
}

GLSLProgram::GLSLProgram(const QString &vertFname, const QString &fragFname)
: m_vertShader(new GLSLShader(vertFname, GL_VERTEX_SHADER)),
  m_fragShader(new GLSLShader(fragFname, GL_FRAGMENT_SHADER))
{
	init();
}

GLSLProgram::GLSLProgram(const GLSLProgram& program)
: m_vertShader(program.m_vertShader),
  m_fragShader(program.m_fragShader)
{
	m_programId = program.m_programId;
	m_isValid = program.m_isValid;
}

GLSLProgram::~GLSLProgram(void)
{
	if (isValid())
	{
		glDetachShader(m_programId, m_vertShader->shaderId());
		glDetachShader(m_programId, m_fragShader->shaderId());
	}

	if (m_programId)
	{
		glDeleteProgram(m_programId);
	}
}

/**
* Sets a single float uniform variable
*
* \param varname The name of the uniform
* \param v0 The value to set the uniform to
*/
void GLSLProgram::setUniform1f(const char* varname, GLfloat v0)
{
	glUniform1f(getUniformLocation(varname), v0);
}

/**
* Sets a 2 float uniform variable
*
* \param varname The name of the uniform
* \param v0 The first value to set the uniform to
* \param v1 The second value to set the uniform to
*/
void GLSLProgram::setUniform2f(const char* varname, GLfloat v0, GLfloat v1)
{
	glUniform2f(getUniformLocation(varname), v0, v1);
}

/**
* Sets a 3 float uniform variable
*
* \param varname The name of the uniform
* \param v0 The first value to set the uniform to
* \param v1 The second value to set the uniform to
* \param v2 The third value to set the uniform to
*/
void GLSLProgram::setUniform3f(const char* varname, GLfloat v0, GLfloat v1, GLfloat v2)
{
	glUniform3f(getUniformLocation(varname), v0, v1, v2);
}

/**
* Sets a 4 float uniform variable
*
* \param varname The name of the uniform
* \param v0 The first value to set the uniform to
* \param v1 The second value to set the uniform to
* \param v2 The third value to set the uniform to
* \param v3 The fourth value to set the uniform to
*/
void GLSLProgram::setUniform4f(const char* varname, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3)
{
	glUniform4f(getUniformLocation(varname), v0, v1, v2, v3);
}

/**
* Sets a 1 int uniform variable
*
* \param varname The name of the uniform
* \param v0 The value to set the uniform to
*/
void GLSLProgram::setUniform1i(const char* varname, GLint v0)
{
	glUniform1i(getUniformLocation(varname), v0);
}

/**
* Sets a 2 int uniform variable
*
* \param varname The name of the uniform
* \param v0 The first value to set the uniform to
* \param v1 The second value to set the uniform to
*/
void GLSLProgram::setUniform2i(const char* varname, GLint v0, GLint v1)
{
	glUniform2i(getUniformLocation(varname), v0, v1);
}

/**
* Sets a 3 int uniform variable
*
* \param varname The name of the uniform
* \param v0 The first value to set the uniform to
* \param v1 The second value to set the uniform to
* \param v2 The third value to set the uniform to
*/
void GLSLProgram::setUniform3i(const char* varname, GLint v0, GLint v1, GLint v2)
{
	glUniform3i(getUniformLocation(varname), v0, v1, v2);
}

/**
* Sets a 4 int uniform variable
*
* \param varname The name of the uniform
* \param v0 The first value to set the uniform to
* \param v1 The second value to set the uniform to
* \param v2 The third value to set the uniform to
* \param v3 The fourth value to set the uniform to
*/
void GLSLProgram::setUniform4i(const char* varname, GLint v0, GLint v1, GLint v2, GLint v3)
{
	glUniform4i(getUniformLocation(varname), v0, v1, v2, v3);
}

/**
*
* \param varname The name of the uniform
* \param count The number of uniforms in the array
* \param value The array of uniforms
*/
void GLSLProgram::setUniform1fv(const char* varname, GLsizei count, GLfloat *value)
{
	glUniform1fv(getUniformLocation(varname), count, value);
}

/**
*
* \param varname The name of the uniform
* \param count The number of uniforms in the array
* \param value The array of uniforms
*/
void GLSLProgram::setUniform2fv(const char* varname, GLsizei count, GLfloat *value)
{
	glUniform2fv(getUniformLocation(varname), count, value);
}

/**
*
* \param varname The name of the uniform
* \param count The number of uniforms in the array
* \param value The array of uniforms
*/
void GLSLProgram::setUniform3fv(const char* varname, GLsizei count, GLfloat *value)
{
	glUniform3fv(getUniformLocation(varname), count, value);
}

/**
*
* \param varname The name of the uniform
* \param count The number of uniforms in the array
* \param value The array of uniforms
*/
void GLSLProgram::setUniform4fv(const char* varname, GLsizei count, GLfloat *value)
{
	glUniform4fv(getUniformLocation(varname), count, value);
}

/**
*
* \param varname The name of the uniform
* \param count The number of uniforms in the array
* \param value The array of uniforms
*/
void GLSLProgram::setUniform1iv(const char* varname, GLsizei count, GLint *value)
{
	glUniform1iv(getUniformLocation(varname), count, value);
}

/**
*
* \param varname The name of the uniform
* \param count The number of uniforms in the array
* \param value The array of uniforms
*/
void GLSLProgram::setUniform2iv(const char* varname, GLsizei count, GLint *value)
{
	glUniform2iv(getUniformLocation(varname), count, value);
}

/**
*
* \param varname The name of the uniform
* \param count The number of uniforms in the array
* \param value The array of uniforms
*/
void GLSLProgram::setUniform3iv(const char* varname, GLsizei count, GLint *value)
{
	glUniform3iv(getUniformLocation(varname), count, value);
}

/**
*
* \param varname The name of the uniform
* \param count The number of uniforms in the array
* \param value The array of uniforms
*/
void GLSLProgram::setUniform4iv(const char* varname, GLsizei count, GLint *value)
{
	glUniform4iv(getUniformLocation(varname), count, value);
}

/**
*
* \param varname The name of the uniform
*/
void GLSLProgram::setUniformMatrix2fv(const char* varname, GLsizei count, GLboolean transpose, GLfloat *value)
{

}

/**
*
* \param varname The name of the uniform
*/
void GLSLProgram::setUniformMatrix3fv(const char* varname, GLsizei count, GLboolean transpose, GLfloat *value)
{

}

/**
*
* \param varname The name of the uniform
*/
void GLSLProgram::setUniformMatrix4fv(const char* varname, GLsizei count, GLboolean transpose, GLfloat *value)
{

}

/**
*
* \param index The index of the vertex attribute
* \param v0 The value to set the attribute to
*/
void GLSLProgram::setVertexAttrib1f(GLuint index, GLfloat v0)
{

}

/**
*
* \param index The index of the vertex attribute
* \param v0 The first value to set the attribute to
* \param v1 The second value to set the attribute to
*/
void GLSLProgram::setVertexAttrib2f(GLuint index, GLfloat v0, GLfloat v1)
{

}

/**
*
* \param index The index of the vertex attribute
* \param v0 The first value to set the attribute to
* \param v1 The second value to set the attribute to
* \param v2 The third value to set the attribute to
*/
void GLSLProgram::setVertexAttrib3f(GLuint index, GLfloat v0, GLfloat v1, GLfloat v2)
{

}

/**
*
* \param index The index of the vertex attribute
* \param v0 The first value to set the attribute to
* \param v1 The second value to set the attribute to
* \param v2 The third value to set the attribute to
* \param v3 The fourth value to set the attribute to
*/
void GLSLProgram::setVertexAttrib4f(GLuint index, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3)
{

}

/**
* Retrieves the location of a uniform variable
*
* \param name The name of the uniform
* \returns The location of the uniform
*/
GLint GLSLProgram::getUniformLocation(const char* name)
{
	GLint location;

	location = glGetUniformLocation(m_programId, name);

	if(location == -1 ) {
		fprintf(stderr, "Uniform variable %s not found\n", name);
		assert(0);
	}

	return(location);
}
